package net.falcon.data;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.Set;

import net.falcon.FalconArena;
import net.falcon.Util;
import net.falcon.data.GlassWall.Wall;
import net.minecraft.server.v1_6_R2.Packet205ClientCommand;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.util.Vector;
import org.kitteh.tag.TagAPI;

public class Arena {


	private ArrayList<Player> players = new ArrayList<Player>();
	private ArrayList<Vector> spawns = new ArrayList<Vector>();
	private List<ItemStack> spawnInv;
	private Integer lives;


	private GameState gameState = GameState.STARTING;
	private static final double GLASS_WALL_LIMIT = 3d;

	public ArrayList<Vector> getSpawns() {
		return spawns;
	}

	public GameState getGameState() {
		return gameState;
	}

	public void setSpawns(ArrayList<Vector> spawns) {
		this.spawns = spawns;
	}

	public List<ItemStack> getSpawnInv() {
		return spawnInv;
	}

	public void setSpawnInv(List<ItemStack> spawnInv) {
		this.spawnInv = spawnInv;
	}

	//a rectangular prism in 3d space can be defined by just two points (opposite corners)
	private Vector pt1;
	private Vector pt2;
	private String name;

	public ArrayList<Player> getPlayers() {
		return players;
	}

	public Integer getMaxPlayers() {
		return spawns.size();
	}

	public Integer getPlayerCount() {
		return players.size();
	}

	public Boolean isFull() {
		return getPlayerCount() >= getMaxPlayers();
	}

	public void refillInventory(Player p) {
		for(ItemStack i: spawnInv) {
			if(i != null) {
				p.getInventory().addItem(i);
			}
		}
	}
	public void spawnPlayer(final Player p) {
		p.setHealth(20);
		p.setFireTicks(0);
		p.setSaturation(3);
		p.setFoodLevel(20);
		p.getInventory().clear();
		Bukkit.getScheduler().runTaskLater(FalconArena.instance, new Runnable() {
			
			public void run() {
				Util.print("RefildledInv.");
				refillInventory(p);
				p.setFireTicks(0);
				p.setVelocity(new Vector(0,0,0));
			}
		}, 5); //5 ticks from now, the player's inv will be refilled.
	}

	public void addPlayer(Player p) {
		broadcast(p.getDisplayName() + " has joined the game!");
		sendPlayerList(p);
		Util.setMetadata(p, "fa.lives", new Integer(lives));
		p.setGameMode(GameMode.ADVENTURE);
		TagAPI.refreshPlayer(p); //gets picked up by PlayerEventListener:onTag and sets tag to ""
		players.add(p);
		spawnPlayer(p);

		Location spawn = spawns.get(players.size()).toLocation(p.getWorld());
		Util.setMetadata(p, "fa.wall.ew", new GlassWall(p, p.getWorld().getSpawnLocation(), Wall.EW));
		Util.setMetadata(p, "fa.wall.ns", new GlassWall(p, p.getWorld().getSpawnLocation(), Wall.NS));
		p.teleport(spawn);

	}
	
	public void sendPlayerList(Player p) {
		Util.sendMessage(p, "Players Ingame:");
		String str = ChatColor.BOLD + "";
		for(Player pa : players) {
			str +=  ", " + pa.getDisplayName();
		}
		Util.sendMessage(p, str);
	}

	public void playerMoved(PlayerMoveEvent e) {
		if(gameState == GameState.STARTING) {
			if(e.getFrom().getX() != e.getTo().getX() || e.getFrom().getZ() != e.getTo().getZ()) {
				//e.setCancelled(true); //locked into your area
				Location resetLoc = e.getFrom().clone();
				resetLoc.setY(e.getTo().getY());
				resetLoc.setPitch(e.getTo().getPitch());
				resetLoc.setYaw(e.getTo().getYaw());
				e.getPlayer().teleport(resetLoc);
			}
		}
		if(gameState == GameState.PLAYING) {
			updateGlassWalls(e);
		}
	}

	public void playerDied(EntityDeathEvent e) {
		Player p = (Player)e.getEntity();
		broadcast(p.getDisplayName() + " has died!");
		e.getDrops().clear();
		Integer lives = (Integer) Util.getMetadata(p, "fa.lives");
		Util.setMetadata(p, "fa.lives", lives - 1);

		if(lives - 1 <= 0) {
			eliminatePlayer(p);
		} else {
			p.setHealth(20); //revive player
			p.setVelocity(new Vector(0,0,0));
			Random r = new Random();
			Integer randIndex = r.nextInt(spawns.size());
			Vector randomSpawn = spawns.get(randIndex);
			p.teleport(randomSpawn.toLocation(p.getWorld()));
			Util.print("Spawning at spawn " + randIndex);
			p.sendMessage("You have " + (lives - 1) + " lives remaining!");
			spawnPlayer(p);
		}

		return;
	}

	public void eliminatePlayer(Player p) {
		players.remove(p);
		Util.setMetadata(p, "fa.map", null);
		TagAPI.refreshPlayer(p);
		broadcast(p.getDisplayName() + " has been eliminated!");
		
		if(players.size() == 1 && !players.get(0).getDisplayName().equals(p.getDisplayName())) {
			Player lastPlayer = players.get(0);
			broadcast(lastPlayer.getDisplayName() + " has WON!");
			Util.setMetadata(lastPlayer, "fa.map", null);
			TagAPI.refreshPlayer(lastPlayer);
			lastPlayer.getInventory().clear();
			gameState = GameState.STARTING;
			players.clear();
		}

		if(players.size() == 0) {
			gameState = GameState.STARTING;
		}
	}
	
	public void blockBroken(BlockBreakEvent e) {
		e.setCancelled(true); //anyone ingame should not be able to break blocks.
	}

	public void updateGlassWalls(PlayerMoveEvent e) {
		//update the EW walls
		Player p = e.getPlayer();

		boolean nearEW = false;
		if(Math.abs(p.getLocation().getZ() - pt1.getZ()) < GLASS_WALL_LIMIT) {
			Location nearestPt = p.getLocation().clone();
			nearestPt.setZ(pt1.getZ());
			((GlassWall)Util.getMetadata(p, "fa.wall.ew")).setLoc(nearestPt.getBlock().getLocation());
			nearEW = true;
		}

		if(Math.abs(p.getLocation().getZ() - pt2.getZ()) < GLASS_WALL_LIMIT) {
			Location nearestPt = p.getLocation().clone();
			nearestPt.setZ(pt2.getZ());
			((GlassWall)Util.getMetadata(p, "fa.wall.ew")).setLoc(nearestPt.getBlock().getLocation());
			nearEW = true;
		}

		if(!nearEW) {
			((GlassWall)Util.getMetadata(p, "fa.wall.ew")).setLoc(new Location(p.getWorld(), 0, -10, 0));
		}

		boolean nearNS = false;
		if(Math.abs(p.getLocation().getX() - pt1.getX()) < GLASS_WALL_LIMIT) {
			Location nearestPt = p.getLocation().clone();
			nearestPt.setX(pt1.getX());
			((GlassWall)Util.getMetadata(p, "fa.wall.ns")).setLoc(nearestPt.getBlock().getLocation());
			nearNS = true;
		}

		if(Math.abs(p.getLocation().getX() - pt2.getX()) < GLASS_WALL_LIMIT) {
			Location nearestPt = p.getLocation().clone();
			nearestPt.setX(pt2.getX());
			((GlassWall)Util.getMetadata(p, "fa.wall.ns")).setLoc(nearestPt.getBlock().getLocation());
			nearNS = true;
		}

		if(!nearNS) {
			((GlassWall)Util.getMetadata(p, "fa.wall.ns")).setLoc(new Location(p.getWorld(), 0, -10, 0));
		}

	}

	public void broadcast(String str) {
		for(Player p : players) {
			Util.sendMessage(p, str);
		}
	}

	public void startGame() {
		gameState = GameState.PLAYING;
		broadcast("The game has begun!");
	}

	public void setPlayers(ArrayList<Player> players) {
		this.players = players;
	}

	public Vector getPt1() {
		return pt1;
	}

	public Vector getPt2() {
		return pt2;
	}

	public int getLives() {
		return lives;
	}

	public void setLives(Integer lives) {
		this.lives = lives;
	}

	public String getName() {
		return name;
	}

	public Arena() {
	}

	public void setPt1(Vector pt) {
		pt1 = pt;
	}

	public void setPt2(Vector pt) {
		pt2 = pt;
	}

	public void setName(String aname) {
		name = aname;
	}

	public boolean addToConfig(Configuration config) {
		if(pt1 == null || pt2 == null || name == null) {
			return false;
		}
		String basePath = "arenas." + name;
		config.set(basePath + ".name", name);
		config.set(basePath + ".pt1", pt1);
		config.set(basePath + ".pt2", pt2);
		return true;
	}



	public enum GameState {
		STARTING,
		PLAYING,
		ENDING;
	}


}
